package dnl.net.netclip;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;

import javax.swing.UIManager;

import org.apache.commons.lang.StringUtils;
import org.apache.mina.common.ConnectFuture;
import org.apache.mina.common.DefaultIoFilterChainBuilder;
import org.apache.mina.common.IoAcceptor;
import org.apache.mina.common.IoAcceptorConfig;
import org.apache.mina.common.IoHandler;
import org.apache.mina.common.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.transport.socket.nio.SocketAcceptor;
import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
import org.apache.mina.transport.socket.nio.SocketConnector;

import dnl.confij.ConfigException;
import dnl.confij.ConfigLoader;
import dnl.confij.ConfigSection;
import dnl.confij.Configuration;
import dnl.logging.LightLog;

/**
 * 
 * 
 * @author Daniel Or
 * 
 */
public class NetClipboard {

	private static final String APP_NAME = "net-clipboard";
	private static final int DEFAULT_PORT = 1234;
	private int port;
	private IoHandler handler;
	private TrayUI ui;
	private Configuration configuration;

	DefaultIoFilterChainBuilder chain;

	public NetClipboard() {
		initConfiguration();
		Integer portFromConfig = readPortFromConfig();
		this.port = portFromConfig == null ? DEFAULT_PORT : portFromConfig;
		init();
	}

	public NetClipboard(int port) {
		this.port = port;
		initConfiguration();
		init();
	}

	private void init() {
		this.handler = new NetClipProtocolHandler();
		ui = new TrayUI(this);
		initServer();
	}

	private void initConfiguration() {
		try {
			configuration = ConfigLoader.getConfiguration(APP_NAME,
					"nclip.config.properties");
			File configDir = ConfigLoader.getConfigDir(APP_NAME, true);
			LightLog.printToFile(new File(configDir, "nclip.log"), true);
		} catch (ConfigException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private Integer readPortFromConfig() {
		ConfigSection networkSection = configuration.getSection("network");
		if (networkSection != null) {
			if (this.port < 0) {
				return networkSection.getInt("port");
			}
		}
		return null;
	}

	public int getPort() {
		return port;
	}

	private void initServer() {
		IoAcceptor acceptor = new SocketAcceptor();
		IoAcceptorConfig config = new SocketAcceptorConfig();
		chain = config.getFilterChain();

		chain.addLast("codec", new ProtocolCodecFilter(
				new ObjectSerializationCodecFactory()));

		SocketAddress address = new InetSocketAddress(port);
		// Bind
		try {
			LightLog.info("Starting NetClip Server on port " + port + " ...");
			acceptor.bind(address, handler, config);
		} catch (IOException e) {
			e.printStackTrace();
			ui.displayErrorMessage("Error", "Could not init server.");
		}
	}

	public Configuration getConfiguration() {
		return configuration;
	}

	public void getClipboardContents(String address) {
		LightLog.info("Retrieving clipboard from:  " + address);
		int ind = address.indexOf(":");
		int port = DEFAULT_PORT;
		String host = address;
		if (ind >= 0) {
			String[] splits = StringUtils.split(address, ":");
			host = splits[0];
			port = Integer.parseInt(splits[1]);
		}
		SocketAddress inetAddress = new InetSocketAddress(host, port);
		SocketConnector socketConnector = new SocketConnector();
		IoAcceptorConfig config = new SocketAcceptorConfig();
		DefaultIoFilterChainBuilder chain2 = config.getFilterChain();

		chain2.addLast("codec", new ProtocolCodecFilter(
				new ObjectSerializationCodecFactory()));

		IoSession session = null;
		try {
			ConnectFuture future1 = socketConnector.connect(inetAddress,
					new GetClipProtocolHandler(ui), config);
			future1.join();
			if (!future1.isConnected()) {
				ui.displayErrorMessage("Error", "Could not connect to: "
						+ address);
			}
			session = future1.getSession();
			ui.showProgress("Retrieving clipboard from: " + address);
			session.write(ClipCommand.GET_CLIP.toString()).join();
			LightLog.info("wrote request into session ... ");
		} catch (Exception e) {
			e.printStackTrace();
			ui.displayErrorMessage("Error", "Could not connect to: " + address);
		}
	}

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) {
			e.printStackTrace();
		}
		String sPort = System.getProperty("port");
		if (sPort != null) {
			new NetClipboard(Integer.parseInt(sPort));
		} else {
			new NetClipboard();
		}
	}

}
